import socket
import platform
import random
import logging


log = logging.getLogger("socket_tools")
logging.basicConfig()
log.setLevel(logging.DEBUG)


def get_host_ip():
    """
    查询本机ip地址
    :return: ip
    """
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
    finally:
        s.close()

    return ip


def get_coding_type():
    # if platform.system() == 'Windows':
    #     coding_type = 'gbk'
    # else:
    #     coding_type = 'utf-8'
    coding_type = 'utf-8'
    return coding_type


# def decode(ss: bytes):
#     try:
#         ret = ss.decode('utf-8')
#     except:
#         ret = ss.decode('gbk')
#     return ret


coding_ls = ['utf-8', 'gbk']


def encode(ss: str, coding_type='utf-8'):
    if isinstance(ss, str):
        try:
            ss = ss.encode(coding_type)
        except Exception as e:
            log.debug(f'coding_type好像不对! [{e}]')
            ss = ss.encode(get_another_coding_type(coding_type))
    return ss


def decode(ss, coding_type='utf-8'):
    """
    获得str类型

    - coding_type只能为['utf-8', 'gbk']之一!
    """
    assert coding_type in coding_ls, f'coding_type必须在coding_ls[{coding_ls}]中!'
    if isinstance(ss, bytes):
        try:
            ss = ss.decode(coding_type)
        except:
            coding_type = get_another_coding_type(coding_type)
            ss = ss.decode(coding_type)
    return ss


def get_another_coding_type(this_coding_type):
    """
    在coding_ls = ['utf-8', 'gbk']中选择另一个coding_type
    """
    assert this_coding_type in coding_ls, f'coding_type必须在coding_ls[{coding_ls}]中!'
    ret = coding_ls.remove(coding_ls)[0]
    return ret


def is_windows_system():
    if platform.system() == 'Windows':
        ret = True
    else:
        ret = False
    return ret


def check_port_in_use(port, host='127.0.0.1'):
    s = None
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(1)
        s.connect((host, int(port)))
        return True
    except socket.error:
        return False
    finally:
        if s:
            s.close()


def get_a_not_in_use_port(port_range=None, try_times=100):
    """
    获得一个未被占用的端口

    :param port_range: 端口范围
    :param try_times: 尝试次数
    :return:
    """
    port = -1

    if not port_range:
        port_range = [1000, 65535]
    else:
        assert isinstance(port_range, (tuple, list)), 'port_range必须为(tuple, list)类型!'

    # 循环绑定端口
    for i in range(try_times):
        try:
            port = random.randint(*port_range)
            if not check_port_in_use(port):
                break
        finally:
            pass
    return port


def send_to(sender, msg, receiver_address):
    return sender.sendto(encode(msg), receiver_address)


def send_close_msg(server_socket, client_address, msg="Close"):
    """
    server_socket发送停止信息给指定地址的客户端udp_socket链接, 让其结束程序.

    :param server_socket: 服务器socket
    :param client_address: 客户端地址
    :param msg: 消息内容
    :return:
    """

    return send_to(server_socket, msg, client_address)


if __name__ == '__main__':
    logging.basicConfig()
    # stun.log.setLevel(logging.DEBUG)

    print(get_host_ip())
